#ifndef BDIBD_H
#define BDIBD_H
#include <cstdio>
#include <cstdlib>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <stdint.h>
#include <zlib.h>
#include "xstdio.h"
#include "throw.h"
#include "linkage.h"
#include "xbdb.h"
#include "genomicpos.h"

typedef uint8_t tid_t;
typedef int32_t position_t;
typedef uint32_t pair_id_t;
typedef double pvalue_t;
typedef double genetic_map_t;

#define COUNT_PVALUE 3


struct PairOfIndividual
	{
	individual_id_t first;
	individual_id_t second;

	PairOfIndividual();
	PairOfIndividual(individual_id_t i1,individual_id_t i2);
	bool contains(individual_id_t id) const;
	individual_id_t complementary(individual_id_t id) const;
	};


struct CoupleOfIndividual
	{
	IndividualPtr first;
	IndividualPtr second;
	CoupleOfIndividual();
	CoupleOfIndividual(IndividualPtr i1,IndividualPtr i2);
	bool contains(IndividualPtr id) const;
	IndividualPtr complementary(IndividualPtr id) const;
	int32_t concordant() const;
	};




std::ostream& operator<<(std::ostream& out,const PairOfIndividual& o);

enum WhichIBD
    {
    IBD_0=0,
    IBD_1=1,
    IBD_2=2
    };

struct Genotype
	{
	pvalue_t pvalue[COUNT_PVALUE];
	Genotype();
	bool hasData() const;
	pvalue_t ibd(WhichIBD w) const;
	pvalue_t ibd0() const;
	};

std::ostream& operator<<(std::ostream& out,const Genotype& o);

struct Position {
	tid_t chrom;
	position_t pos;
	} ;
std::ostream& operator<<(std::ostream& out,const Position& o);

class Marker
	{
	public:
		std::string rsId;
		Position position;
		std::vector<genetic_map_t> geneticMaps;
	};
std::ostream& operator<<(std::ostream& out,const Marker& o);

class LinkageRow
    {
    private:
	uint32_t len;
	Genotype* array;
    public:
	LinkageRow(uint32_t size);
	LinkageRow(const bdb::Entry* entry);
	~LinkageRow();
	uint32_t size() const;
	const Genotype* getGenotype(pair_id_t pairid) const;
	pvalue_t ibd(pair_id_t pairid,WhichIBD whichibd) const;
	pvalue_t ibd0(pair_id_t pairid) const;
	void setGenotype(pair_id_t pairid,const Genotype* genotype);

    };


class IBDEnv
	{
	public:
		DB_TXN *txn;
		bool readOnly;
		/** berkeleydb environment */
		bdb::Environment env;
		/** maps family id to Family */
		bdb::Database* familyDb;
		/** maps individual id to Individual */
		bdb::Database* individualsDb;
		/** maps tid to chromosome name */
		bdb::Database* chromName2TidDb;
		/** maps pair id to struct PairofIndividual */
		bdb::Database* pairIdToPairOfIndividualsDb;
		/** table map individual-id back to pairId */
		bdb::Database* pairOfIndividualsToPairId;
		/** bigLinkage */
		bdb::Database* linkageDB;
		/** rs2pos */
		bdb::Database* rsToPositionDb;
		/** position2marker */
		bdb::Database* pos2markerDb;
		/** pairId2theoritical */
		bdb::Database* pairId2theoriticalDb;
		/** metadata */
		bdb::Database* metadataDB;
		

		std::map<std::string,Family*> name2family;
		std::map<family_id_t,Family*> id2family;
		std::map<individual_id_t,Individual*> id2individual;
		std::map<tid_t,std::string> id2chromosome;
		std::map<std::string,tid_t> chrom2tid;
		uint32_t count_pairs;
		bool debug;
		FamilyPtr entryToFamily(const bdb::Entry* entry);
		void familyToEntry(const FamilyPtr family,bdb::Entry* entry);
		IndividualPtr entryToIndividual(const bdb::Entry* entry);
		void individualToEntry(const IndividualPtr individual,bdb::Entry* entry);
		void mappingSplit(const std::string& s,std::vector<std::string>& tokens);
		Marker* entryToMarker(const bdb::Entry* entry);
		void markerToEntry(const Marker* marker, bdb::Entry* entry);
		Family* findFamilyByName(const char* s);
		int findTidByName(const char* chromName);
		IndividualPtr findIndividualById(individual_id_t id);
		CoupleOfIndividual findIndividualsByPair(PairOfIndividual* pair);
		std::auto_ptr<Marker> findMarkerByPosition(const Position* position);
		std::auto_ptr<Genotype> findTheoriticalIBDByPairId(pair_id_t);
		pair_id_t findPairIdByPair(PairOfIndividual* pair);

		IBDEnv();
		~IBDEnv();
		void open(char* dir,bool readOnly);
		void close();
		void readReskin(const char* file);
		void readPedigree(const char* file);
		void readMapping(const char* file);
		void readBed(const char* file);
		void readData(std::set<std::string>& datafiles);
		void putMetadata(std::string key,std::string value);
		static int comparePositions(DB *db, const DBT *dbt1, const DBT *dbt2);
		static int comparePairs(DB *db, const DBT *dbt1, const DBT *dbt2);

	private:
		void _prefillTids();
	};



class Application
	{
	protected:
		Application();
		IBDEnv ibdEnv;
		/** berkeleyDB home */
		char* db_home;

		int checkHome();

	public:
		virtual ~Application();
		virtual void usage(std::ostream& out)=0;
		virtual int main(int argc,char** argv)=0;
		IBDEnv* env();
	};

class ApplicationBuilder:public Application
	{
	protected:
		char* reskinfile;
		char* pedfile;
		char* mapfile;
		char* datafiles;
		char* bedfile;
	public:
		ApplicationBuilder();
		virtual ~ApplicationBuilder();
		virtual void usage(std::ostream& out);
		virtual int main(int argc,char** argv);
	};

class ApplicationReader:public Application
	{
	protected:
		virtual bool accept_family_id(family_id_t famid);
		virtual bool accept_individual_id(individual_id_t indid);
		virtual bool accept_pair_id(pair_id_t indid);
		virtual bool accept_marker(const Marker* marker);
		virtual bool accept_position(const Position* pos);
		virtual bool accept_pvalue_at_index(int p);
		virtual bool accept_pair_self_self();
		virtual bool print_theoritical_pvalue();
	public:
		enum output_mode_type
		       {
		       ONE_PAIR_PER_LINE,
		       ALL_PAIRS_ON_LINE
		       };
		output_mode_type output_mode;
		bool accept_pvalue_at[COUNT_PVALUE];
		bool print_self_self;
		bool print_theoritical_proba;
		pvalue_t min_pvalue_ibd0;
		std::set<family_id_t> restric_family_ids;
		std::set<pair_id_t> restric_pair_ids;
		std::set<individual_id_t> restric_individuals_ids;
		std::ostream* output;
		std::ostream* errstream;
		void resultHeader1();
		int scanPosition(const Position* position);
		void scanRegion(GenomicPosition* position);
		void readRsList(Reader* reader);
		void proba_christian();
		ApplicationReader();
		virtual ~ApplicationReader();
		virtual void usage(std::ostream& out);
		virtual int main(int argc,char** argv);
	};

#endif
